package org.tinycloud.tinyjob.client;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinycloud.tinyjob.client.interceptor.JobSubject;
import org.tinycloud.tinyjob.client.interceptor.JobSubjectHolder;
import org.tinycloud.tinyjob.client.utils.JsonUtils;
import org.tinycloud.tinyjob.client.utils.secure.SecureUtils;


import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * <p>
 * tiny-job核心业务帮助类
 * </p>
 *
 * @author liuxingyu01
 * @since 2025/9/27 22:19
 */
public class TinyJobHelper {
    private final static Logger logger = LoggerFactory.getLogger(TinyJobHelper.class);

    private final static String registerUrl = "/admin/register";
    private final static String heartbeatUrl = "/admin/heartbeat";
    private final static String handleResultUrl = "/admin/handleResult";

    /**
     * 缓存全局配置信息
     */
    public static final Map<String, TinyJobExecutorConfig> CONFIG_CACHE = new ConcurrentHashMap<>(1);
    public static final String CONFIG_CACHE_KEY = "global_config_key";


    public static void register(TinyJobExecutorConfig config) {
        if (StringUtils.isEmpty(config.getAdminAddresses()) || StringUtils.isEmpty(config.getWorkerName()) || Objects.isNull(config.getProjectId())) {
            logger.info("TinyJobHelper register failed, please check the parameter");
            return;
        }
        String url = getRandomAddress(config.getAdminAddresses()) + registerUrl;
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("ip", config.getIp());
        paramMap.put("port", config.getPort());
        paramMap.put("workerName", config.getWorkerName());
        paramMap.put("projectId", config.getProjectId());
        paramMap.put("address", config.getAddress());

        Map<String, Object> otherMap = new HashMap<>();
        otherMap.put("connectTimeout", config.getConnectTimeout());
        otherMap.put("socketTimeout", config.getSocketTimeout());

        Map<String, Object> headerMap = SecureUtils.genSecurityHeader(config.getAccessToken());
        String result = postJson(url, paramMap, headerMap, otherMap);
        // 判断是否注册成功，200为成功
        if (StringUtils.isNotEmpty(result)) {
            CONFIG_CACHE.putIfAbsent(CONFIG_CACHE_KEY, config);
        }
    }

    public static void heartbeat(TinyJobExecutorConfig config) {
        if (StringUtils.isEmpty(config.getAdminAddresses()) || StringUtils.isEmpty(config.getWorkerName()) || Objects.isNull(config.getProjectId())) {
            logger.info("TinyJobHelper heartbeat failed, please check the parameter");
            return;
        }
        String url = getRandomAddress(config.getAdminAddresses()) + heartbeatUrl;
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("ip", config.getIp());
        paramMap.put("port", config.getPort());
        paramMap.put("workerName", config.getWorkerName());
        paramMap.put("projectId", config.getProjectId());
        paramMap.put("address", config.getAddress());

        Map<String, Object> otherMap = new HashMap<>();
        otherMap.put("connectTimeout", config.getConnectTimeout());
        otherMap.put("socketTimeout", config.getSocketTimeout());

        Map<String, Object> headerMap = SecureUtils.genSecurityHeader(config.getAccessToken());
        String result = postJson(url, paramMap, headerMap, otherMap);
    }

    /**
     * 任务处理成功
     *
     * @param handleMsg 处理结果信息
     * @return 处理结果
     */
    public static boolean handleSuccess(String handleMsg) {
        return handleResult("0", handleMsg);
    }

    /**
     * 任务处理失败
     *
     * @param handleMsg 处理结果信息
     * @return 处理结果
     */
    public static boolean handleFail(String handleMsg) {
        return handleResult("1", handleMsg);
    }

    /**
     * @param handleStatus 执行状态（0成功， 1失败）
     * @param handleMsg    处理结果信息
     * @return 处理结果
     */
    public static boolean handleResult(String handleStatus, String handleMsg) {
        TinyJobExecutorConfig config = CONFIG_CACHE.get(CONFIG_CACHE_KEY);
        String url = getRandomAddress(config.getAdminAddresses()) + handleResultUrl;
        JobSubject jobSubject = JobSubjectHolder.getJobSubject();

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("jobSubject", jobSubject);
        paramMap.put("handleStatus", handleStatus);
        paramMap.put("handleMsg", handleMsg);

        Map<String, Object> otherMap = new HashMap<>();
        otherMap.put("connectTimeout", config.getConnectTimeout());
        otherMap.put("socketTimeout", config.getSocketTimeout());

        Map<String, Object> headerMap = SecureUtils.genSecurityHeader(config.getAccessToken());
        String result = postJson(url, paramMap, headerMap, otherMap);

        return true;
    }


    /**
     * post-json
     *
     * @param url       请求地址
     * @param paramMap  请求参数
     * @param headerMap 请求头
     * @return 请求结果
     */
    public static String postJson(String url, Map<String, Object> paramMap, Map<String, Object> headerMap, Map<String, Object> otherMap) {
        int retryTimes = (otherMap == null || otherMap.get("failRetryTimes") == null) ? 0 : Integer.parseInt(otherMap.get("failRetryTimes").toString());
        int socketTimeout = (otherMap == null || otherMap.get("socketTimeout") == null) ? 5 * 1000 : Integer.parseInt(otherMap.get("socketTimeout").toString());
        int connectTimeout = (otherMap == null || otherMap.get("connectTimeout") == null) ? 3 * 1000 : Integer.parseInt(otherMap.get("connectTimeout").toString());

        CloseableHttpClient httpclient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String result = null;
        // 超时时间设置
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout).build();

        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        // 设置请求参数和请求头
        String jsonParam = JsonUtils.writeValueAsString(paramMap);
        if (StringUtils.isNotEmpty(jsonParam)) {
            StringEntity entity = new StringEntity(jsonParam, StandardCharsets.UTF_8);
            // entity.setContentEncoding("UTF-8"); // 这个参数设置有问题，部分接口在设置这个参数后无法访问
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
        }
        if (null != headerMap && !headerMap.isEmpty()) {
            for (Map.Entry<String, Object> entry : headerMap.entrySet()) {
                httpPost.addHeader(entry.getKey(), entry.getValue().toString());
            }
        }
        try {
            // 发送请求
            int retryCount = 0;
            do {
                response = httpclient.execute(httpPost);
                result = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                if (response.getStatusLine().getStatusCode() != 200) {
                    logger.error("Error in postJson Request URL is [{}], params [{}]. Result:[{}]", url, jsonParam, result);
                    HttpClientUtils.closeQuietly(response);
                    retryCount++;
                } else {
                    break;
                }
            } while (retryCount <= retryTimes);
            if (retryCount > retryTimes) {
                throw new RuntimeException("Exceeded maximum retry attempts");
            }
        } catch (Exception e) {
            logger.error("Error in postJson ", e);
        } finally {
            HttpClientUtils.closeQuietly(response);
            HttpClientUtils.closeQuietly(httpclient);
        }
        return result;
    }


    /**
     * 从逗号分隔的地址字符串中随机获取一个有效地址
     *
     * @param addressStr 逗号分隔的地址字符串（如 "http://127.0.0.1:8080,http://127.0.0.1:8081"）
     * @return 随机选中的单个地址；若输入无效或无有效地址，返回 null
     */
    private static String getRandomAddress(String addressStr) {
        if (StringUtils.isEmpty(addressStr)) {
            return null;
        }
        String[] addressArray = addressStr.split(",");
        List<String> validAddresses = new ArrayList<>(addressArray.length);
        for (String addr : addressArray) {
            String trimmedAddr = addr.trim();
            if (StringUtils.isNotEmpty(trimmedAddr)) {
                validAddresses.add(trimmedAddr);
            }
        }
        if (validAddresses.isEmpty()) {
            return null;
        }
        int randomIndex = ThreadLocalRandom.current().nextInt(validAddresses.size());
        return validAddresses.get(randomIndex);
    }

}
